package com.agileai.hotweb.bizmoduler.core;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.agileai.common.KeyGenerator;
import com.agileai.domain.DataParam;
import com.agileai.domain.DataRow;
import com.agileai.domain.db.Column;
import com.agileai.domain.db.Table;
import com.agileai.hotweb.common.Constants;
import com.agileai.util.ListUtil;
import com.agileai.util.StringUtil;

public abstract class MasterSubServiceImpl extends BaseService implements MasterSubService{
	public static String PRIMARY_KEY = "primaryKey";
	public static String PK_GEN_POLICY = "pkGenPolicy";
	
	protected Map<String,String> subTableIdNameMapping = new HashMap<String,String>();
	protected Map<String,String> subTableIdSortFieldMapping = new HashMap<String,String>();
	protected HashMap<String,HashMap<String,String>> tableNamePersistenceInfoMapping = new HashMap<String,HashMap<String,String>>(); 
	
	public MasterSubServiceImpl(){
		super();
	}
	protected String getPrimaryKey(String tableName){
		String result = null; 
		if (!tableNamePersistenceInfoMapping.containsKey(tableName)){
			this.initPersistenceInfo(tableName);			
		}
		HashMap<String,String> persistenceInfo = tableNamePersistenceInfoMapping.get(tableName);
		result = persistenceInfo.get(PRIMARY_KEY);
		return result;
	}
	protected String getPkGenPolicy(String tableName){
		String result = null; 
		if (!tableNamePersistenceInfoMapping.containsKey(tableName)){
			this.initPersistenceInfo(tableName);			
		}
		HashMap<String,String> persistenceInfo = tableNamePersistenceInfoMapping.get(tableName);
		result = persistenceInfo.get(PK_GEN_POLICY);
		return result;
	}
	public void createMasterRecord(DataParam param) {
		String statementId = sqlNameSpace+"."+"insertMasterRecord";
		processDataType(param, tableName);
		processPrimaryKeys(tableName,param);
		this.daoHelper.insertRecord(statementId, param);
	}
	public void createMasterRecord(DataParam param, String pKeyValue) {
		String statementId = sqlNameSpace+"."+"insertMasterRecord";
		processDataType(param, tableName);
		param.put(getPrimaryKey(tableName),pKeyValue);
		this.daoHelper.insertRecord(statementId, param);
	}	
	public DataRow getMasterRecord(DataParam param) {
		String statementId = sqlNameSpace+"."+"getMasterRecord";
		DataRow result = this.daoHelper.getRecord(statementId, param);
		return result;
	}
	public void updateMasterRecord(DataParam param) {
		String statementId = sqlNameSpace+"."+"updateMasterRecord";
		processDataType(param, tableName);
		this.daoHelper.updateRecord(statementId, param);
	}
	public void deleteClusterRecords(DataParam param) {
		String statementId = sqlNameSpace+"."+"deleteMasterRecord";
		this.daoHelper.deleteRecords(statementId, param);
		String[] tableIds = this.getTableIds();
		for (int i=0;i < tableIds.length;i++){
			String tableId = tableIds[i];
			if (BASE_TABLE_ID.equals(tableId))continue;
			statementId = sqlNameSpace+"."+"delete"+StringUtil.upperFirst(tableId)+"Records";
			this.daoHelper.deleteRecords(statementId, param);
		}
	}
	public List<DataRow> findMasterRecords(DataParam param) {
		String statementId = sqlNameSpace+"."+"findMasterRecords";
		List<DataRow> result = this.daoHelper.queryRecords(statementId, param);
		return result;
	}
	protected void processPrimaryKeys(String tableName,DataParam param){
		String primaryKey = getPrimaryKey(tableName);
		String pkGenPolicy = getPkGenPolicy(tableName);
		KeyGenerator keyGenerator = new KeyGenerator();
		if (Constants.PKType.INCREASE.equals(pkGenPolicy)){
			param.put(primaryKey,keyGenerator.getMaxId(tableName, primaryKey,getDataSource()));
		}
		else if(Constants.PKType.CHARGEN.equals(pkGenPolicy)){
			String genPk = keyGenerator.genKey();
			param.put(primaryKey,genPk);
		}
	}
	protected synchronized void initPersistenceInfo(String tableName){
		if (!tableNamePersistenceInfoMapping.containsKey(tableName)){
			Table table = getTableMetaData(tableName);
			List<Column> columns = table.getColumns();
			HashMap<String,String> persistenceInfo = new HashMap<String,String>();
			String primaryKey = null;
			int count = columns.size();
			for (int i=0;i < count;i++){
				Column column = columns.get(i);
				if (column.isPK()){
					primaryKey = column.getName();
					persistenceInfo.put(PRIMARY_KEY, primaryKey);
					break;
				}
			}
			String pkGenPolicy = null;
			for (int i=0;i < count;i++){
				Column column = columns.get(i);
				if (primaryKey != null
						&& primaryKey.equals(column.getName())){
					if ("java.lang.Integer".equals(column.getClassName())
							|| "java.lang.Long".equals(column.getClassName())){
						pkGenPolicy = Constants.PKType.INCREASE;
						break;
					}
					else if ("java.lang.String".equals(column.getClassName())){
						if (column.getLength()%36 == 0 && column.getLength() < 150){
							pkGenPolicy = Constants.PKType.CHARGEN;
							break;
						}
						else{
							pkGenPolicy = Constants.PKType.ASSIGN;
							break;
						}
					}
				}
			}
			persistenceInfo.put(PK_GEN_POLICY, pkGenPolicy);
			tableNamePersistenceInfoMapping.put(tableName, persistenceInfo);
		}
	}
	
	protected void processDataType(DataParam dataParam,String tableName){
		super.processDataType(dataParam, tableName);
		this.initPersistenceInfo(tableName);
	}

	public List<DataRow> findSubRecords(String subId, DataParam param) {
		List<DataRow> result = null;
		String statementId = sqlNameSpace+"."+"find"+StringUtil.upperFirst(subId)+"Records";
		result = this.daoHelper.queryRecords(statementId, param);
		return result;
	}
	public void saveSubRecords(DataParam param, List<DataParam> insertRecords, List<DataParam> updateRecords) {
		String subId = param.get("currentSubTableId");
		String curTableName = subTableIdNameMapping.get(subId);
		if (insertRecords != null && insertRecords.size() > 0){
			String sortField = subTableIdSortFieldMapping.get(subId);
			int sortValue = 0;
			if (!StringUtil.isNullOrEmpty(sortField)){
				sortValue = getNewMaxSort(subId,param);	
			}
			for (int i=0;i < insertRecords.size();i++){
				DataParam paramRow = insertRecords.get(i);
				processDataType(paramRow, curTableName);	
				processPrimaryKeys(curTableName,paramRow);
				
				if (!StringUtil.isNullOrEmpty(sortField)){
					paramRow.put(sortField,sortValue+i);
				}
			}
		}
		String statementId = sqlNameSpace+"."+"insert"+StringUtil.upperFirst(subId)+"Record";
		this.daoHelper.batchInsert(statementId, insertRecords);
		
		statementId = sqlNameSpace+"."+"update"+StringUtil.upperFirst(subId)+"Record";
		this.daoHelper.batchUpdate(statementId, updateRecords);
	}
	
	public void deleteSubRecord(String subId, DataParam param) {
		String statementId = sqlNameSpace+"."+"delete"+StringUtil.upperFirst(subId)+"Record";
		this.daoHelper.deleteRecords(statementId, param);
	}
	
	public DataRow getSubRecord(String subId, DataParam param) {
		String statementId = sqlNameSpace+"."+"get"+StringUtil.upperFirst(subId)+"Record";
		DataRow result = this.daoHelper.getRecord(statementId, param);
		return result;
	}

	public void createSubRecord(String subId, DataParam param) {
		String curTableName = subTableIdNameMapping.get(subId);
		processDataType(param, curTableName);	
		processPrimaryKeys(curTableName,param);
		processSubSortField(subId,param);
		
		String statementId = sqlNameSpace+"."+"insert"+StringUtil.upperFirst(subId)+"Record";
		this.daoHelper.insertRecord(statementId, param);
	}

	public void updateSubRecord(String subId, DataParam param) {
		String statementId = sqlNameSpace+"."+"update"+StringUtil.upperFirst(subId)+"Record";
		String curTableName = subTableIdNameMapping.get(subId);
		processDataType(param, curTableName);
		this.daoHelper.updateRecord(statementId, param);
	}	
	
	abstract public String[] getTableIds();

	public void setSubTableIdNameMapping(
			HashMap<String, String> subTableIdNameMapping) {
		this.subTableIdNameMapping = subTableIdNameMapping;
	}
	
	public void setSubTableIdSortFieldMapping(
			Map<String, String> subTableIdSortFieldMapping) {
		this.subTableIdSortFieldMapping = subTableIdSortFieldMapping;
	}
	
	protected void processSubSortField(String subId,DataParam param){
		String sortField = subTableIdSortFieldMapping.get(subId);
		if (!StringUtil.isNullOrEmpty(sortField)){
			int sortValue = getNewMaxSort(subId,param);
			param.put(sortField,sortValue);
		}
	}
	
	protected int getNewMaxSort(String subId,DataParam param){
		int result = 0;
		String sortField = subTableIdSortFieldMapping.get(subId);
		String statementId = sqlNameSpace+"."+"find"+StringUtil.upperFirst(subId)+"Records";
		List<DataRow> records = this.daoHelper.queryRecords(statementId, param);
		if (records != null && records.size() > 0){
			DataRow row = records.get(records.size()-1);
			String maxMenuSort = row.stringValue(sortField);
			result = Integer.parseInt(maxMenuSort)+1;
		}else{
			result = 1;
		}
		return result;
	}
	
	public void changeCurrentSort(DataParam param, boolean isUp) {
		String subId = param.get("currentSubTableId");
		String statementId = sqlNameSpace+"."+"find"+StringUtil.upperFirst(subId)+"Records";
		List<DataRow> records = this.daoHelper.queryRecords(statementId, param);
		String tableName = subTableIdNameMapping.get(subId);
		String idField = getPrimaryKey(tableName);
		String currentId = retrieveRecordId(idField,param);
		String sortField = subTableIdSortFieldMapping.get(subId);
		
		DataRow curRow = null;
		String curSort = null;
		if (isUp){
			DataRow beforeRow = null;
			for (int i=0;i < records.size();i++){
				DataRow row = records.get(i);
				String tempMenuId = row.stringValue(idField);
				if (currentId.equals(tempMenuId)){
					curRow = row;
					beforeRow = records.get(i-1);
					break;
				}
			}
			curSort = curRow.stringValue(sortField);
			String beforeSort = beforeRow.stringValue(sortField);;
			curRow.put(sortField,beforeSort);
			beforeRow.put(sortField,curSort);
			this.updateSubRecord(subId,curRow.toDataParam(true));
			this.updateSubRecord(subId,beforeRow.toDataParam(true));
		}else{
			DataRow nextRow = null;
			for (int i=0;i < records.size();i++){
				DataRow row = records.get(i);
				String tempMenuId = row.stringValue(idField);
				if (currentId.equals(tempMenuId)){
					curRow = row;
					nextRow = records.get(i+1);
					break;
				}
			}
			curSort = curRow.stringValue(sortField);
			String nextSort = nextRow.stringValue(sortField);
			curRow.put(sortField,nextSort);
			nextRow.put(sortField,curSort);
			this.updateSubRecord(subId,curRow.toDataParam(true));
			this.updateSubRecord(subId,nextRow.toDataParam(true));
		}
	}

	protected String retrieveRecordId(String idField,DataParam param){
		String result = null;
		String currentRecordIndex = param.get("currentRecordIndex");
		if (!StringUtil.isNullOrEmpty(currentRecordIndex)){
			result = param.get(idField+"_"+currentRecordIndex);			
		}else{
			result = param.get(idField);
		}
		return result;
	}
	
	public boolean isFirstChild(String subId,DataParam param) {
		boolean result = false;
		String statementId = sqlNameSpace+"."+"find"+StringUtil.upperFirst(subId)+"Records";
		List<DataRow> records = this.daoHelper.queryRecords(statementId, param);
		if (!ListUtil.isNullOrEmpty(records)){
			String tableName = subTableIdNameMapping.get(subId);
			String idField = getPrimaryKey(tableName);
			String currentId = retrieveRecordId(idField,param);
			DataRow firstRow = records.get(0);
			String tempMenuId = firstRow.stringValue(idField);
			if (currentId.equals(tempMenuId)){
				result = true;
			}
		}
		return result;
	}
	
	public boolean isLastChild(String subId,DataParam param) {
		boolean result = false;
		String statementId = sqlNameSpace+"."+"find"+StringUtil.upperFirst(subId)+"Records";
		List<DataRow> records = this.daoHelper.queryRecords(statementId, param);
		if (!ListUtil.isNullOrEmpty(records)){
			String tableName = subTableIdNameMapping.get(subId);
			String idField = getPrimaryKey(tableName);
			String currentId = retrieveRecordId(idField,param);
			DataRow firstRow = records.get(records.size()-1);
			String tempMenuId = firstRow.stringValue(idField);
			if (currentId.equals(tempMenuId)){
				result = true;
			}
		}
		return result;
	}
}
